#include "ChatGrpcClient.hpp"
#include "Defer.hpp"
#include "const.hpp"
#include "message.grpc.pb.h"
#include <cstddef>
#include <grpcpp/channel.h>
#include <grpcpp/client_context.h>
#include <grpcpp/create_channel.h>
#include <grpcpp/security/credentials.h>
#include <grpcpp/support/status.h>
#include <iostream>
#include <memory>
#include <mutex>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
ChatConPool::ChatConPool(size_t poolSize, std::string host, std::string port)
    : _poolSize(poolSize), _host(host), _port(port), _stop(false) {
  std::shared_ptr<Channel> channel = grpc::CreateChannel(
      _host + ":" + _port, grpc::InsecureChannelCredentials());
  for (size_t i = 0; i < _poolSize; i++) {
    _connections.push(ChatService::NewStub(channel));
  }
}

ChatConPool::~ChatConPool() {
  std::lock_guard<std::mutex> lock(_mutex);
  close();
  while (!_connections.empty()) {
    _connections.pop();
  }
}

void ChatConPool::close() {
  _stop = true;
  _cond.notify_all();
}

std::unique_ptr<ChatService::Stub> ChatConPool::getConnection() {
  std::unique_lock<std::mutex> lock(_mutex);
  _cond.wait(lock, [this] {
    if (_stop) {
      return true;
    } else {
      return !_connections.empty();
    }
  });
  // 判断是否关闭
  if (_stop) {
    return nullptr;
  }
  std::unique_ptr<ChatService::Stub> con = std::move(_connections.front());
  _connections.pop();
  return con;
}

void ChatConPool::returnConnection(std::unique_ptr<ChatService::Stub> con) {
  std::unique_lock<std::mutex> lock(_mutex);
  if (_stop) {
    return;
  }
  _connections.push(std::move(con));
  _cond.notify_one();
}

ChatGrpcClient::ChatGrpcClient() {
  std::string peerServer = config.get<std::string>("PeerServer.Servers", "");
  // 没有其它服务器
  if (peerServer.empty()) {
    std::cout << "PeerServer is empty!" << std::endl;
    return;
  }
  // 获取其他聊天服务器的名称 不同服务器名称之间用 "," 分隔
  std::vector<std::string> serverNames;
  std::stringstream ss(peerServer);
  std::string serverName;
  while (std::getline(ss, serverName, ',')) {
    serverNames.push_back(serverName);
  }
  for (std::string &sn : serverNames) {
    std::string serverHost = config.get<std::string>(sn + ".Host", "");
    std::string rpcServerPort = config.get<std::string>(sn + ".RPCPort", "");
    if (serverHost.empty() || rpcServerPort.empty()) {
      continue;
    }
    std::string rpc_server_address = serverHost + ":" + rpcServerPort;
    _pools[rpc_server_address] =
        std::make_unique<ChatConPool>(5, serverHost, rpcServerPort);
  }
}

AddFriendRsp ChatGrpcClient::NotifyAddFriend(std::string serverIp,
                                             const AddFriendReq req) {
  AddFriendRsp rsp;
  Defer defer([&rsp, &req]() {
    rsp.set_error(ErrorCodes::Success);
    rsp.set_applyuid(req.applyuid());
    rsp.set_touid(req.touid());
  });
  auto iter = _pools.find(serverIp);
  if (iter == _pools.end()) {
    std::cerr << "Not find rpc server" << std::endl;
    return rsp;
  }
  auto &pool = iter->second;
  ClientContext context;
  // 从池子中拿到一个连接
  auto stub = pool->getConnection();
  Defer d([&stub, &pool]() { pool->returnConnection(std::move(stub)); });
  Status status = stub->NotifyAddFriend(&context, req, &rsp);
  if (!status.ok()) {
    rsp.set_error(ErrorCodes::RPCFailed);
    return rsp;
  }
  return rsp;
}

AuthFriendRsp ChatGrpcClient::NotifyAuthFriend(std::string serverIp,
                                               const AuthFriendReq) {
  AuthFriendRsp rsp;
  return rsp;
}

bool ChatGrpcClient::GetBaseInfo(std::string baseKey, int uid,
                                 std::shared_ptr<UserInfo> &userInfo) {
  return false;
}

TextChatMsgRsp ChatGrpcClient::NotifyTextChatMsg(std::string serverIp,
                                                 const TextChatMsgReq req) {

  TextChatMsgRsp rsp;
  auto iter = _pools.find(serverIp);
  if (iter == _pools.end()) {
    std::cerr << "Not find rpc server" << std::endl;
    return rsp;
  }
  auto &pool = iter->second;
  ClientContext context;
  // 从池子中拿到一个连接
  auto stub = pool->getConnection();
  Defer d([&stub, &pool]() { pool->returnConnection(std::move(stub)); });
  Status status = stub->NotifyTextChatMsg(&context, req, &rsp);
  if (!status.ok()) {
    rsp.set_error(ErrorCodes::RPCFailed);
    return rsp;
  }
  return rsp;
}
